﻿using FrameworkCore.Extensions;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using NM.Module.Auth.API.Configurations;
using NM.Module.Auth.API.Interfaces;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace NM.Module.Auth.API.Services;

public class TokenService : ITokenService
{
    private readonly IOptions<JwtSettings> _optJwtSettings;
    public TokenService(IOptions<JwtSettings> optJwtSettings)
    {
        _optJwtSettings = optJwtSettings;
    }

    public Task<(string token, int expires)> BuildAsync(IEnumerable<Claim> claims, JwtSettings? options = null, bool isRefreshToken = false)
    {
        options ??= _optJwtSettings.Value;

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.SecurityKey));
        var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        (DateTime expires, int expireSeconds) = GetExpirationInfo(options, isRefreshToken);

        // 添加过期时间声明
        claims = claims.Append(new Claim("exp", expires.ToUnixTimestampSeconds().ToString())).ToList();

        // 创建 Token
        var token = new JwtSecurityToken(
            issuer: options.Issuer,
            audience: options.Audience,
            claims: claims,
            notBefore: DateTime.UtcNow,
            expires: expires,
            signingCredentials: signingCredentials
        );

        return Task.FromResult((new JwtSecurityTokenHandler().WriteToken(token), expireSeconds));
    }

    public JwtSecurityToken Decode(string jwtToken)
    {
        var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
        var jwtSecurityToken = jwtSecurityTokenHandler.ReadJwtToken(jwtToken);
        return jwtSecurityToken;
    }

    private static (DateTime expires, int expireSeconds) GetExpirationInfo(JwtSettings options, bool isRefreshToken)
    {
        if (isRefreshToken)
        {
            var expires = DateTime.UtcNow.AddSeconds(options.RefreshExpiresSeconds);
            return (expires, options.RefreshExpiresSeconds);
        }
        else
        {
            var expires = DateTime.UtcNow.AddSeconds(options.ExpireSeconds);
            return (expires, options.ExpireSeconds);
        }
    }
}
